﻿using System;
using fastCSharp;
using fastCSharp.net.tcp.http;
using System.Drawing.Imaging;
using System.IO;
using fastCSharp.threading;

namespace diantou.web.server
{
    /// <summary>
    /// 图片文件服务
    /// </summary>
    sealed class imageServer : fastCSharp.net.tcp.http.domainServer.staticFileServer
    {
        /// <summary>
        /// 网站生成配置
        /// </summary>
        /// <returns>网站生成配置</returns>
        protected override fastCSharp.code.webConfig getWebConfig()
        {
            return webServer.WebConfig;
        }
        /// <summary>
        /// 图片文件扩展名
        /// </summary>
        private static readonly keyValue<int, byte[]>[] imageContentTypes = new keyValue<int, byte[]>[]
        {
            new keyValue<int, byte[]>('.' + ('j' << 8) + ('p' << 16) + ('g' << 24), fastCSharp.web.contentTypeInfo.GetContentType("jpg")), 
            new keyValue<int, byte[]>(0, null), 
            new keyValue<int, byte[]>('j' + ('p' << 8) + ('e' << 16) + ('g' << 24), fastCSharp.web.contentTypeInfo.GetContentType("jpeg")),
            new keyValue<int, byte[]>(0, null),
            new keyValue<int, byte[]>('.' + ('g' << 8) + ('i' << 16) + ('f' << 24), fastCSharp.web.contentTypeInfo.GetContentType("gif")), 
            new keyValue<int, byte[]>(0, null), 
            new keyValue<int, byte[]>('.' + ('b' << 8) + ('m' << 16) + ('p' << 24), fastCSharp.web.contentTypeInfo.GetContentType("bmp")), 
            new keyValue<int, byte[]>('.' + ('p' << 8) + ('n' << 16) + ('g' << 24), fastCSharp.web.contentTypeInfo.GetContentType("png"))
        };
        /// <summary>
        /// JPGE文件类型信息
        /// </summary>
        private static byte[] jpegConntentType = fastCSharp.web.contentTypeInfo.GetContentType("jpeg");
        /// <summary>
        /// 生成缩略图输出信息
        /// </summary>
        /// <param name="data">图片数据</param>
        /// <param name="width">缩略图宽度</param>
        /// <param name="height">缩略图高度</param>
        /// <param name="contentType">图片类型信息</param>
        /// <param name="response">输出信息</param>
        /// <returns>缩略图输出信息</returns>
        private response thumbnail(subArray<byte> data, int width, int height, byte[] contentType, response response)
        {
            if (response == null) response = response.Get();
            subArray<byte> thumbnailData = fastCSharp.drawing.thumbnail.Pad(data, width, height, ImageFormat.Jpeg, System.Drawing.Color.White, null, WebConfig.IsFileCacheHeader ? fileCache.HttpHeaderSize : 0);
            response.State = response.state.Ok200;
            response.CacheControl = cacheControl;
            if (thumbnailData.Count == 0)
            {
                response.SetBody(data, true, WebConfig.IsFileCacheHeader);
                response.ContentType = contentType;
            }
            else
            {
                response.SetBody(thumbnailData, true, WebConfig.IsFileCacheHeader);
                response.BodyFile = null;
                response.ContentType = jpegConntentType;
            }
            return response;
        }
        /// <summary>
        /// 图片文件异步读取
        /// </summary>
        private sealed class imageReader
        {
            /// <summary>
            /// 图片文件服务
            /// </summary>
            private imageServer server;
            /// <summary>
            /// HTTP套接字
            /// </summary>
            private fastCSharp.net.tcp.http.socketBase socket;
            /// <summary>
            /// HTTP请求头部
            /// </summary>
            private fastCSharp.net.tcp.http.requestHeader request;
            /// <summary>
            /// 会话标识
            /// </summary>
            private long socketIdentity;
            /// <summary>
            /// 输出信息
            /// </summary>
            private response response;
            /// <summary>
            /// 图片类型信息
            /// </summary>
            private byte[] contentType;
            /// <summary>
            /// 缩略图宽度
            /// </summary>
            private int width;
            /// <summary>
            /// 缩略图高度
            /// </summary>
            private int height;
            /// <summary>
            /// 图片文件流
            /// </summary>
            private FileStream fileStream;
            /// <summary>
            /// 读取异步回调委托
            /// </summary>
            private AsyncCallback onRead;
            /// <summary>
            /// 图片数据
            /// </summary>
            private subArray<byte> data;
            /// <summary>
            /// 图片文件异步读取
            /// </summary>
            private imageReader() { onRead = read; }
            /// <summary>
            /// 开始读取图片文件
            /// </summary>
            public void Read()
            {
                try
                {
                    fileStream = new FileStream(response.BodyFile, FileMode.Open, FileAccess.Read, FileShare.Read, fastCSharp.config.appSetting.StreamBufferSize, FileOptions.Asynchronous);
                    int length = (int)fileStream.Length, dataLength = data.UnsafeArray.length(), headerSize = server.WebConfig.IsFileCacheHeader ? fileCache.HttpHeaderSize : 0;
                    log.Default.Add(response.BodyFile + " 图像文件无缓存[" + length.toString() + @"]
referer: " + request.Referer.ToArray().deSerialize(), new System.Diagnostics.StackFrame(), true);
                    if (dataLength < headerSize + length) data.UnsafeSet(new byte[headerSize + length], headerSize, length);
                    else data.UnsafeSet(headerSize, length);
                    fileStream.BeginRead(data.UnsafeArray, data.StartIndex, length, onRead, this);
                    return;
                }
                catch (Exception error)
                {
                    log.Default.Add(error, response.BodyFile, false);
                }
                read();
            }
            /// <summary>
            /// 图片文件读取回调
            /// </summary>
            /// <param name="result"></param>
            private void read(IAsyncResult result)
            {
                try
                {
                    int length;
                    using (fileStream) length = fileStream.EndRead(result);
                    if (length == data.Count)
                    {
                        server.thumbnail(data, width, height, contentType, response);
                        if (response.BodyData != data.UnsafeArray) server.setCache(request, response, jpegConntentType);
                    }
                }
                catch (Exception error)
                {
                    log.Default.Add(error, null, false);
                }
                read();
            }
            /// <summary>
            /// 图片文件读取完毕处理
            /// </summary>
            private void read()
            {
                try
                {
                    if (request.IsRange && !request.FormatRange(response.BodySize)) socket.ResponseError(socketIdentity, response.state.RangeNotSatisfiable416);
                    else socket.Response(socketIdentity, ref response);
                }
                finally
                {
                    server = null;
                    socket = null;
                    request = null;
                    response = null;
                    typePool<imageReader>.PushNotNull(this);
                }
            }
            /// <summary>
            /// 图片文件异步读取
            /// </summary>
            /// <param name="server">图片文件服务</param>
            /// <param name="socket">HTTP套接字</param>
            /// <param name="socketIdentity">会话标识</param>
            /// <param name="request">HTTP请求头部</param>
            /// <param name="response">输出信息</param>
            /// <param name="width">缩略图宽度</param>
            /// <param name="height">缩略图高度</param>
            /// <param name="contentType">图片类型信息</param>
            /// <returns>图片文件异步读取</returns>
            public static imageReader Get(imageServer server, fastCSharp.net.tcp.http.socketBase socket, long socketIdentity
                , fastCSharp.net.tcp.http.requestHeader request, response response, int width, int height, byte[] contentType)
            {
                imageReader imageReader = typePool<imageReader>.Pop() ?? new imageReader();
                imageReader.server = server;
                imageReader.socket = socket;
                imageReader.socketIdentity = socketIdentity;
                imageReader.request = request;
                imageReader.response = response;
                imageReader.width = width;
                imageReader.height = height;
                imageReader.contentType = contentType;
                return imageReader;
            }
        }
        /// <summary>
        /// HTTP客户端请求处理
        /// </summary>
        /// <param name="socket">HTTP套接字</param>
        /// <param name="socketIdentity">会话标识</param>
        /// <param name="request">HTTP请求头部</param>
        protected override unsafe void request(fastCSharp.net.tcp.http.socketBase socket, long socketIdentity, fastCSharp.net.tcp.http.requestHeader request)
        {
            response response = file(request);
            if (response == null)
            {
                try
                {
                    if (request.Method == fastCSharp.web.http.methodType.GET && request.ContentLength == 0)
                    {
                        subArray<byte> path = request.Path;
                        if (path.Count > 12 && path.Count <= fastCSharp.io.file.MaxFullNameLength)
                        {
                            fixed (byte* pathFixed = path.UnsafeArray)
                            {
                                byte* pathStart = pathFixed + path.StartIndex;
                                if (*(int*)pathStart == '/' + ('t' << 8) + ('i' << 16) + ('_' << 24))
                                {
                                    byte* pathEnd = pathStart + path.Count - sizeof(int);
                                    keyValue<int, byte[]> contentType = imageContentTypes[(*(pathEnd + 2) >> 1) & 7];
                                    if (*(int*)pathEnd == contentType.Key && (*pathEnd == '.' || *(pathEnd - 1) == '.'))
                                    {
                                        int width = 0;
                                        byte* current = pathStart + sizeof(int);
                                        while ((uint)(*current - '0') < 10)
                                        {
                                            width *= 10;
                                            width += *current++ - '0';
                                        }
                                        if (*current == '_')
                                        {
                                            int height = 0;
                                            while ((uint)(*++current - '0') < 10)
                                            {
                                                height *= 10;
                                                height += *current - '0';
                                            }
                                            if (*current == '/' && (width | height) != 0 && width >= 0 && height >= 0)
                                            {
                                                int index = (int)(current - pathStart);
                                                request.UnsafeSetPath(path.StartIndex + index, path.Count - index);
                                                fileCache fileCache = file(request, ref response);
                                                request.UnsafeSetPath(path.StartIndex, path.Count);
                                                if (fileCache != null)
                                                {
                                                    response = thumbnail(fileCache.Data, width, height, fileCache.ContentType, null);
                                                    response.LastModified = fileCache.LastModified;
                                                    if (response.BodyData != fileCache.Data.UnsafeArray) setCache(request, response, jpegConntentType);
                                                }
                                                else if (response != null && response.BodyFile != null)
                                                {
                                                    imageReader.Get(this, socket, socketIdentity, request, response, width, height, contentType.Value).Read();
                                                    return;
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
                catch (Exception error)
                {
                    fastCSharp.log.Default.Add(error, null, false);
                }
            }
            if (response != null) socket.Response(socketIdentity, ref response);
            else socket.ResponseError(socketIdentity, fastCSharp.net.tcp.http.response.state.NotFound404);
        }
    }
}
